package org.rowan.adi.tda.tda2d;

import java.util.LinkedList;
import org.rowan.adi.Vector;
import org.rowan.adi.linearalgebra.LinearAlgebra2D;
import org.rowan.adi.components.comp2d.Component2D;
import org.rowan.adi.components.comp2d.polygon.Polygon;
import org.rowan.adi.tda.tda2d.services.Service2D;

/**
 * The DecisionManager2D class provides functionality for a decision aid to determine
 * collision detection between foreign 2D components and the high and low-level 2D
 * components. The DecisionManager2D is able to perform services on the lower-level
 * components.
 *
 * @author Robert Russell
 * @version 1.0
 */
public abstract class DecisionManager2D {

    protected Polygon highLevelComponent; // Represents the area of the decision aid.
    protected LinkedList<Polygon> lowLevelComponents; // Components within the high level area.
    protected LinkedList<Service2D> services; // Services to be performed on the low-level components.

    /**
     * Adds the given Service2D to the DecisionManager2D. This Service2D will
     * now be performed.
     * @param service the instance of Service2D to be added.
     */
    public final void addService(Service2D service) {
        if (!services.contains(service)) {
            services.add(service);
        }
    }

    /**
     * Determines whether the child Component2D is wholly contained by the high-level Component2D.
     * @param child the child Component2D.
     * @return <code>true</code> if the child Component2D is wholly contained by
     * the high-level Component2D; <code>false</code> otherwise.
     */
    public final boolean highLevelContains(Component2D child) {
        return LinearAlgebra2D.contains(highLevelComponent, child);
    }

    /**
     * Computes the closest distance between the high-level Component2D and the
     * Component2D given.
     * @param component an instance of Component2D
     * @return the closest distance between the high-level Component2D and the
     * Component2D given.
     */
    public final double highLevelDistance(Component2D component) {
        return LinearAlgebra2D.distance(highLevelComponent, component);
    }

    /**
     * Determines whether the moving Component2D will intersect the high-level
     * Component2D within a given amount time
     * @param moving an instance of Component2D which has a non-zero trajectory.
     * @param time the the maximum amount of time given for the Component2D
     * instances to intersect.
     * @return <code>true</code> if the moving Component2D will intersect the
     * high-level Component2D in a given amount of time; <code>false</code> otherwise.
     */
    public final boolean highLevelIntersect(Component2D moving, double time) {
        return LinearAlgebra2D.intersect(moving, highLevelComponent, time);
    }

    /**
     * Computes the cross-sectional area of the high-level Component2D with the
     * Component2D given.
     * @param component an instance of Component2D.
     * @return the cross-sectional area of the high-level Component2D with the
     * Component2D given.
     */
    public final Component2D highLevelIntersect(Component2D component) {
        return LinearAlgebra2D.intersect(highLevelComponent, component);
    }

    /**
     * Determines whether the child Component2D is wholly contained by the low-level Component2D.
     * @param lowLevel a low-level Component2D within this DecisionManager2D.
     * @param child the child Component2D.
     * @return <code>true</code> if the child Component2D is wholly contained by
     * the low-level Component2D; <code>false</code> otherwise.
     * @throws IllegalArgumentException If the low-level component provided does not
     * exist within the high-level decision space.
     */
    public final boolean lowLevelContains(Polygon lowLevel, Component2D child) {
        if (this.lowLevelComponents.contains(lowLevel)) {
            return LinearAlgebra2D.contains(lowLevel, child);
        } else {
            throw new IllegalArgumentException("The low-level component provided does" +
                    "not exist within the high-level decision space.");
        }
    }

    /**
     * Computes the closest distance between the low-level Component2D and the
     * Component2D given.
     * @param lowLevel a low-level Component2D within this DecisionManager2D.
     * @param component an instance of Component2D
     * @return the closest distance between the low-level Component2D and the
     * Component2D given.
     * @throws IllegalArgumentException If the low-level component provided does not
     * exist within the high-level decision space.
     */
    public final double lowLevelDistance(Polygon lowLevel, Component2D component) {
        if (this.lowLevelComponents.contains(lowLevel)) {
            return LinearAlgebra2D.distance(lowLevel, component);
        } else {
            throw new IllegalArgumentException("The low-level component provided does" +
                    "not exist within the high-level decision space.");
        }
    }

    /**
     * Determines whether the moving Component2D will intersect the low-level
     * Component2D within a given amount time
     * @param lowLevel a low-level Component2D within this DecisionManager2D.
     * @param moving an instance of Component2D which has a non-zero trajectory.
     * @param time the the maximum amount of time given for the Component2D
     * instances to intersect.
     * @return <code>true</code> if the moving Component2D will intersect the
     * low-level Component2D in a given amount of time; <code>false</code> otherwise.
     * @throws IllegalArgumentException If the low-level component provided does not
     * exist within the high-level decision space.
     */
    public final boolean lowLevelIntersect(Polygon lowLevel, Component2D moving, double time) {
        if (this.lowLevelComponents.contains(lowLevel)) {
            return LinearAlgebra2D.intersect(moving, lowLevel, time);
        } else {
            throw new IllegalArgumentException("The low-level component provided does" +
                    "not exist within the high-level decision space.");
        }
    }

    /**
     * Computes the cross-sectional area of the low-level Component2D with the
     * Component2D given.
     * @param lowLevel a low-level Component2D within this DecisionManager2D.
     * @param component an instance of Component2D.
     * @return the cross-sectional area of the low-level Component2D with the
     * Component2D given.
     * @throws IllegalArgumentException If the low-level component provided does not
     * exist within the high-level decision space.
     */
    public final Component2D lowLevelIntersect(Polygon lowLevel, Component2D component) {
        if (this.lowLevelComponents.contains(lowLevel)) {
            return LinearAlgebra2D.intersect(lowLevel, component);
        } else {
            throw new IllegalArgumentException("The low-level component provided does" +
                    "not exist within the high-level decision space.");
        }
    }

    /**
     * Manually move a low-level Component2D within this DecisionManager2D to a
     * new location in two dimensional space.
	 * @param location the new location.
     * @param shape the instance of Polygon that will be relocated.
     */
    public final void moveComponent(Vector location, Polygon shape) {
        if (!lowLevelComponents.contains(shape)) {
            shape.setLocation(location);
        }
    }

    /**
     * Performs all the service provided by all the Service2D instances that
     * this DecisionManager2D contains.
     */
    public void perform() {
        for (Service2D service : services) {
            service.service();
        }
    }

    /**
     * Removes the given Service2D from DecisionManager2D. This Service2D will
     * no longer be performed.
     * @param service the Service2D to be removed.
     */
    public final void removeService(Service2D service) {
        if (!services.contains(service)) {
            services.remove(service);
        }
    }
}
